import json
from typing import List, Optional

from app.core.config import settings
from app.core.redis_conn import redis_helper
from app.schemas.job import SchedulerJob, JobExecutionLog, RedisJobExecutionResult


class RedisStore:
    """
    设计目的是减少有关于SQLDB数据库的压力
    用户在接口提交了操作后会实时同步到Redis中
    调度器不直接对SQLDB进行操作,而是对Redis进行操作,
    任务执行的历史记录也存储在其中,在规定时间后将记录一次性同步到SQLDB中。

    Key Concepts:
    - 任务元数据(Job Metadata): 存储任务的配置信息，包括：
      * 任务ID
      * 任务名称
      * 执行间隔
      * 触发器类型
      * 最后执行时间
      * 下次执行时间
      * 任务状态
      * 优先级
      ...

    - 任务执行历史记录(Job Execution History): 存储任务执行的历史记录，包括：
      * 任务ID
      * 任务名称
      * 执行时间
      * 执行结果
      * 执行状态
      ...

    RedisStore 类提供了对这些数据结构的操作接口，包括添加、更新、删除任务，以及同步任务数据到SQLDB。

    """

    TASK_EXEC_HISTORY_SYNC_INTERVAL = 10  # 每10秒同步一次
    JOB_META_KEY = "scheduler:job_meta"  # 任务元数据 MAP = {job_id: job_data}
    JOB_EXEC_HISTORY_KEY = (
        "scheduler:job_exec_history"  # 任务执行历史记录 LIST = [{job_id: job_id, is_sync: false ...record...}, ...]
    )

    def __init__(self):
        self.redis = redis_helper.get_connection(settings.TASK_REDIS_DB)

    async def add_job(self, job_data: SchedulerJob) -> bool:
        # 插入任务元数据
        res = await self.redis.hset(self.JOB_META_KEY, str(job_data.id), json.dumps(job_data.model_dump(), default=str))
        return res > 0

    async def get_job(self, job_id: int) -> Optional[SchedulerJob]:
        # 获取任务元数据
        job_data = await self.redis.hget(self.JOB_META_KEY, str(job_id))

        if not job_data:
            return None

        return SchedulerJob.model_validate(json.loads(job_data))

    async def get_jobs(self, job_ids: List[int]) -> List[SchedulerJob]:
        # 获取任务元数据
        if not job_ids:
            return []
        str_job_ids = [str(job_id) for job_id in job_ids]
        job_datas = await self.redis.execute_command("HMGET", self.JOB_META_KEY, *str_job_ids)
        return [SchedulerJob.model_validate(json.loads(job_data)) for job_data in job_datas if job_data]

    async def update_job(self, job_id: int, job_data: SchedulerJob) -> bool:
        # 更新任务元数据
        res = await self.redis.hset(self.JOB_META_KEY, str(job_id), json.dumps(job_data.model_dump(), default=str))
        return res > 0

    async def delete_job(self, job_id: int) -> bool:
        # 删除任务元数据
        res = await self.redis.hdel(self.JOB_META_KEY, str(job_id))
        return res > 0

    async def get_all_jobs(self) -> List[SchedulerJob]:
        # 获取所有任务元数据
        job_ids = await self.redis.hkeys(self.JOB_META_KEY)
        jobs = []
        for job_id in job_ids:
            job_data = await self.redis.hget(self.JOB_META_KEY, job_id)
            if job_data:
                jobs.append(SchedulerJob.model_validate(json.loads(job_data)))
        return jobs

    async def clear_jobs(self):
        # 清空所有任务元数据
        await self.redis.delete(self.JOB_META_KEY)

    async def add_job_execution_log(self, job_id: int, log: RedisJobExecutionResult) -> bool:
        # 添加任务执行历史记录
        res = await self.redis.lpush(self.JOB_EXEC_HISTORY_KEY, json.dumps(log.model_dump(), default=str))
        return res > 0

    async def clear_job_execution_logs(self):
        await self.redis.delete(self.JOB_EXEC_HISTORY_KEY)

    async def get_job_execution_logs(self, job_id: int = None) -> List[RedisJobExecutionResult]:
        """获取任务执行历史记录

        Args:
            job_id: 任务ID, 如果为None, 则获取所有任务执行历史记录
        """
        # 获取任务执行历史记录
        logs = await self.redis.lrange(self.JOB_EXEC_HISTORY_KEY, 0, -1)
        result = []
        if job_id:
            for log in logs:
                log_data = json.loads(log)
                if log_data.get("job_id") == job_id:
                    result.append(RedisJobExecutionResult.model_validate(log_data))
        else:
            for log in logs:
                result.append(RedisJobExecutionResult.model_validate(json.loads(log)))
        return result

    async def get_all_job_ids(self) -> List[int]:
        # 获取所有任务ID
        return [int(job_id) for job_id in await self.redis.hkeys(self.JOB_META_KEY)]
